#pragma once

// Dependencies
class Polygon2;
#include "Point2.h"
#include <deque>
#include <vector>
#include <algorithm>

class Tessellation2 {
public: // Structures
	template<class Tag>
	struct Mode {
		static const unsigned value;
	};

	struct TriangleStripTag;
	struct TriangleFanTag;
	struct TrianglesTag;

	struct Primitive {
		unsigned mode;
		std::vector<Point2> vertices;
		Primitive( unsigned m ) : mode(m) { }
		bool operator < ( const Primitive& p ) const {
			return mode < p.mode;
		}
	};

public:
	void swap( std::vector<Primitive>& tess ) {
		mPrimitives.swap( tess );
	}

public: // Visitors
	// F should be a functor or function that takes three const Point2_t<double>&s
	template<class F>
	void eachTriangle( F& f ) const {
		// Primitives are (or should be) sorted by their type
		// Therefore, we can gain (some) performance by operating on batches, instead of individually
		auto rangeBegin = mPrimitives.begin();
		auto rangeEnd = rangeBegin;
		auto end = mPrimitives.end();

		while ( rangeEnd != end ) {
			while ( rangeEnd != end && rangeBegin->mode == rangeEnd->mode)
				rangeEnd++;

			if ( rangeBegin->mode == Tessellation2::Mode<Tessellation2::TrianglesTag>::value )
				trianglesProc( rangeBegin, rangeEnd, f );
			else if ( rangeBegin->mode == Tessellation2::Mode<Tessellation2::TriangleStripTag>::value )
				trianglestripProc( rangeBegin, rangeEnd, f);
			else if ( rangeBegin->mode == Tessellation2::Mode<Tessellation2::TriangleFanTag>::value )
				trianglefanProc( rangeBegin, rangeEnd, f);

			rangeBegin = rangeEnd;
		}
	}

	// F should be a functor that takes a const Tessellation::Primitive&
	template<class F>
	void eachPrimitive( F& f ) const {
		std::for_each( primitives().begin(), primitives().end(), f );
	}

private: // Helper functions
	template<class Iterator, class F>
	static void trianglesProc( Iterator begin, Iterator end, F& f ) {
		std::for_each( begin, end, [&] (const Primitive& p ) {
			const std::vector<Point2>& vec = p.vertices;
			size_t ct = vec.size();
			for ( size_t idx = 2; idx < ct; idx += 3 )
				f( vec[idx-2], vec[idx-1], vec[idx] );
		} );
	}

	template<class Iterator, class F>
	static void trianglestripProc( Iterator begin, Iterator end, F& f ) {
		std::for_each( begin, end, [&] ( const Primitive& p ) {
			const std::vector<Point2>& vec = p.vertices;
			size_t ct = vec.size();
			f( vec[0], vec[1], vec[2] );
			for ( size_t idx = 3; idx < ct; idx ++ )
				f( p.vertices[idx-2], p.vertices[idx-1], p.vertices[idx] );
		} );
	}

	template<class Iterator, class F>
	static void trianglefanProc( Iterator begin, Iterator end, F& f ) {
		std::for_each( begin, end, [&] ( const Primitive& p ) {
			const std::vector<Point2>& vec = p.vertices;
			size_t ct = vec.size();
			for ( size_t idx = 2; idx < ct; ++idx )
				f( vec[0], vec[idx-1], vec[idx] );
		} );
	}

public: // Accessors
	const std::vector<Primitive>& primitives() const { return mPrimitives; }	

private: // Members
	std::vector<Primitive> mPrimitives;
};